#pragma once
#include <OpenXLSX/OpenXLSX.hpp>
#include <fstream>
#include <iostream>
#include <vector>
#include <unordered_set>
#include "logger.hpp"
#include "utils.hpp"

namespace zhiang {

class ExcelTable {
private:
    std::string get_cell_value_as_string(const OpenXLSX::XLCell& cell) {
        try {
            OpenXLSX::XLCellValue value = cell.value();
            if (value.type() == OpenXLSX::XLValueType::Empty) {
                return "";
            } else if (value.type() == OpenXLSX::XLValueType::String) {
                return value.get<std::string>();
            } else if (value.type() == OpenXLSX::XLValueType::Integer) {
                return std::to_string(value.get<int64_t>());
            } else if (value.type() == OpenXLSX::XLValueType::Float) {
                return std::to_string(value.get<double>());
            } else if (value.type() == OpenXLSX::XLValueType::Boolean) {
                return value.get<bool>() ? "true" : "false";
            }
        } catch (const std::exception& e) {
            LOG_ERROR("获取单元格值失败: {}", e.what());
        }
        return "ERROR";
    }
public:
    ExcelTable() {}
    ~ExcelTable() {}
    ExcelTable(const int row_nums, const int column_nums, const std::string& excel_name, 
        const std::string& excel_path = "./", const std::string& excel_sheet = "Sheet1") :
        _row_nums(row_nums),
        _column_nums(column_nums),
        _excel_path(excel_path),
        _excel_name(excel_name),
        _excel_sheet(excel_sheet),
        _table(row_nums + 1, std::vector<std::string>(column_nums + 1)) {
        OpenXLSX::XLDocument readDoc;
        readDoc.open(excel_path + excel_name);
        auto readWks = readDoc.workbook().worksheet(excel_sheet);
        
        for (int row = 1; row <= _row_nums; ++row) {
            for (int col = 1; col <= _column_nums; ++col) {
                std::string cell_address = number_to_letter(col) + std::to_string(row);
                OpenXLSX::XLCell cell = readWks.cell(cell_address);
                _table[row][col] = get_cell_value_as_string(cell);
            }
        }
        LOG_DEBUG("读取Excel[{}{}-{}]内容已完成", excel_path, excel_name, excel_sheet);
        readDoc.close();
    }

    void deduplicate_row(const std::vector<std::string> column_letters) {
        std::vector<int> columns;
        for(auto& s : column_letters) columns.push_back(letter_to_number(s));
        LOG_DEBUG("开始排序");
        sort(_table.begin(), _table.end(), [&](const std::vector<std::string>& v1, const std::vector<std::string>& v2)->bool {
            for(int& i : columns) {
                if(v1[i] < v2[i]) return true;
            }
            return false;
        });
        auto eq = [&](const std::vector<std::string>& v1, const std::vector<std::string>& v2)->bool {
            for(int& i : columns) {
                if(v1[i] != v2[i]) return false;
            }
            return true;
        };
        LOG_DEBUG("去重ing");
        int left = 0, right = 1;
        for(; right <= _row_nums; right++) {
            LOG_DEBUG("left:{}   right: {}", left, right);
            if(eq(_table[left], _table[right])) continue;
            _table[++left] = _table[right];
        }
        _row_nums = left;
    }

    void save(const std::string& excel_name, const std::string& excel_path = "./", const std::string& excel_sheet = "Sheet1") {
        OpenXLSX::XLDocument doc;
        doc.create(excel_path + excel_name);
        auto wks = doc.workbook().worksheet(excel_sheet);
        for (int row = 1; row <= _row_nums; ++row) {
            for (int col = 1; col <= _column_nums; ++col) {
                std::string cell_address = number_to_letter(col) + std::to_string(row);
                wks.cell(cell_address).value() = _table[row][col];
            }
        }
        doc.save();
        doc.close();
        LOG_DEBUG("储存Excel[{}{}-{}]内容已完成", excel_path, excel_name, excel_sheet);
    }
private:
    int _row_nums = 0;    // 行
    int _column_nums = 0; // 列
    std::string _excel_name;
    std::string _excel_path;
    std::string _excel_sheet;
    std::vector<std::vector<std::string>> _table;
};

}